class MazeImp {
	constructor(m = 99, n = 99) {
		if (m % 2 === 0 || n % 2 === 0) {
			throw (new Error('迷宫行和列必须为奇数!'));
		}
		const cvs = document.getElementById('cvs');
		cvs.width = document.body.clientWidth - 10 > 600 ? 600 : 300;
		cvs.height = cvs.width;
		this.m = cvs.width < 600 ? 33 : m;
		this.n = cvs.width < 600 ? 33 : n;
		this.ctx = cvs.getContext('2d');
		this.width = cvs.width;
		this.height = cvs.height;
		this.wall = '#';
		this.road = '0';
		this.entryX = 0;
		this.entryY = 1;
		this.outX = this.m - 1;
		this.outY = this.n - 2;
		this.dirction = [[1, 0], [-1, 0], [0, 1], [0, -1]];
	}

	sleep(time) {
		return new Promise((resolve) => setTimeout(() => resolve(), time));
	}

	start() {
		this.over = false;
		this.maze = new Array(this.m);
		this.visited = new Array(this.m);
		this.inMist = new Array(this.m);
		for (let m = 0; m < this.m; m++) {
			this.maze[m] = new Array(this.n);
			this.visited[m] = new Array(this.n);
			this.inMist[m] = new Array(this.n);
			for (let n = 0; n < this.n; n++) {
				if (m % 2 === 1 && n % 2 === 1) {
					this.maze[m][n] = this.road;
				} else {
					this.maze[m][n] = this.wall;
				}
				this.visited[m][n] = false;
				this.inMist[m][n] = true;
			}
		}
		this.maze[this.entryX][this.entryY] = this.road;
		this.maze[this.outX][this.outY] = this.road;
		this.calc(this.entryX + 1, this.entryY);
	}
  
	clear() {
		this.queue = [];
		this.over = true;
		this.ctx.clearRect(0, 0, this.width, this.height);
	}

	inArea(x, y) {
		return x >= 0 && x < this.m && y >= 0 && y < this.n;
	}

	async calc(x, y) {
		if (!this.inArea(x, y)) {
			return console.log(`(${x},${y})此点超出界限！`);
		}
		this.queue = new Array();
		this.queue.push([x, y]);
		this.visited[x][y] = true;
		this.openMist(x, y);
		this.render();
		while (this.queue.length !== 0) {
			let randomIndex = Math.floor(Math.random() * this.queue.length);
			let randomE = this.queue[randomIndex];
			let lastE = this.queue[this.queue.length - 1];
			this.queue[randomIndex] = lastE;
			this.queue.pop();
			for (let i = 0; i < 4; i++) {
				if(this.over && this.queue.length === 0) return;
				let newX = randomE[0] + this.dirction[i][0] * 2;
				let newY = randomE[1] + this.dirction[i][1] * 2;
				this.openMist(newX, newY);
				if (this.inArea(newX, newY) && !this.visited[newX][newY]) {
					this.queue.push([newX, newY]);
					this.visited[newX][newY] = true;
					await this.sleep(10);
					this.update(randomE[0] + this.dirction[i][0], randomE[1] + this.dirction[i][1]);
				}
			}
		}
		this.over = true;
	}

	openMist(x, y) {
		if (this.inArea(x, y)) {
			for (let m = x - 1; m <= x + 1; m++) {
				for (let n = y - 1; n <= y + 1; n++) {
					if (this.inArea(m, n)) {
						this.inMist[m][n] = false;
					}
				}
			}
		}
	}

	update(x, y) {
		if (this.inArea(x, y)) {
			this.maze[x][y] = this.road;
		}
	}

	render() {
		if (this.over) return;
		this.ctx.clearRect(0, 0, this.width, this.height);
		this.ctx.beginPath();
		let w = this.width / this.m;
		let h = this.height / this.n;
		for (let m = 0; m < this.m; m++) {
			for (let n = 0; n < this.n; n++) {
				if (this.inMist[m][n]) {
					this.ctx.fillStyle = '#000';
				} else if (this.maze[m][n] === '#') {
					this.ctx.fillStyle = 'blue';
				} else if (this.maze[m][n] === '0') {
					this.ctx.fillStyle = '#fff';
				}
				let x = m * w;
				let y = n * h;
				this.ctx.fillRect(x, y, w, h);
			}
		}
		window.requestAnimationFrame(() => {
			this.render();
		});
	}

}

export default MazeImp;